package com.bub.pay.core.util;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.io.ByteArrayOutputStream;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;

public class EncryptUtil {

    private static String DEFAULT_CHARSET = "utf-8";
    private static int RSA_KEY_SIZE = 1024;
    private static int AES_KEY_SIZE = 256;
    private static int ENCRYPT_SEGMENT_SIZE = 117;
    private static int DECRYPT_SEGMENT_SIZE = 128;
    private static String privateKey = "";
    private static String publicKey = "";


    /**
     * 初始化RSA密钥
     * @throws NoSuchAlgorithmException
     */
    public static void initRSAKeyPair() throws NoSuchAlgorithmException {
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
        keyPairGenerator.initialize(RSA_KEY_SIZE , new SecureRandom());
        KeyPair keyPair = keyPairGenerator.generateKeyPair();
        PrivateKey pairPrivate = keyPair.getPrivate();
        PublicKey pairPublic = keyPair.getPublic();
        privateKey = Base64.getEncoder().encodeToString(pairPrivate.getEncoded());
        publicKey = Base64.getEncoder().encodeToString(pairPublic.getEncoded());

    }

    /**
     * RSA公钥加密
     * @param data
     * @param encodedPublic
     * @return
     * @throws Exception
     */
    public static String encryptByRSAPublic(String data , String encodedPublic) throws Exception{

        PublicKey publicKey = KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(Base64.getDecoder().decode(encodedPublic)));

        Cipher cipher = Cipher.getInstance("RSA");

        cipher.init(Cipher.ENCRYPT_MODE,publicKey);

        byte[] bytes = doFinalSegment(cipher, data.getBytes(), ENCRYPT_SEGMENT_SIZE);

        String result = Base64.getEncoder().encodeToString(bytes);

        return result;
    }

    /**
     * RSA私钥解密
     * @param encodedData
     * @param encodedPrivate
     * @return
     * @throws Exception
     */
    public static String decryptByRSAPrivate(String encodedData , String encodedPrivate) throws Exception{

        PrivateKey privateKey = KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(Base64.getDecoder().decode(encodedPrivate)));

        Cipher cipher = Cipher.getInstance("RSA");

        cipher.init(Cipher.DECRYPT_MODE,privateKey);

        byte[] bytes = doFinalSegment(cipher, Base64.getDecoder().decode(encodedData), DECRYPT_SEGMENT_SIZE);

        String result = new String(bytes);

        return result;
    }

    /**
     * 分段加解密
     * @param cipher
     * @param data
     * @param segment
     * @return
     * @throws Exception
     */
    public static byte[] doFinalSegment(Cipher cipher , byte[] data , int segment) throws Exception {

        ByteArrayOutputStream out = new ByteArrayOutputStream();
        int offset = 0;
        byte[] buffer;
        int i = 0;
        while(data.length - offset > 0){
            if(data.length - offset > segment){
                buffer = cipher.doFinal(data, offset, segment);
            } else {
                buffer = cipher.doFinal(data, offset, data.length - offset);
            }
            i ++;
            offset = i * segment;
            out.write(buffer,0,buffer.length);
        }
        byte[] result = out.toByteArray();
        out.close();
        return result;
    }

    /**
     * AES加解密
     * @param data
     * @param key  对称密钥
     * @param mode 1 加密  2 解密
     * @return
     * @throws Exception
     */
    public static String AES(String data , String key , int mode) throws Exception{

        KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
        keyGenerator.init(AES_KEY_SIZE,new SecureRandom(key.getBytes()));
        SecretKey secretKey = keyGenerator.generateKey();//直接初始化生成的的密钥
        SecretKeySpec spec = new SecretKeySpec(secretKey.getEncoded(),"AES");//SecretKeySpec是指通过某种加密方法(AES or Rijndael)加密后的密钥
        boolean encrypt = mode == Cipher.ENCRYPT_MODE;
        Cipher cipher = Cipher.getInstance("AES");
        cipher.init(mode,spec);
        byte[] dataBytes;
        if(encrypt){
            dataBytes = data.getBytes();
            byte[] bytes = cipher.doFinal(dataBytes);
            return Base64.getEncoder().encodeToString(bytes);
        } else {
            dataBytes = Base64.getDecoder().decode(data);
            byte[] bytes = cipher.doFinal(dataBytes);
            return new String(bytes,DEFAULT_CHARSET);
        }
    }


    public static void main(String[] args) throws Exception{
        String encodingString = "{\n" +
                "\t\"name\":\"XXX\",\n" +
                "\t\"firstName\":\"XXX\",\n" +
                "\t\"lastName\":\"XXX\",\n" +
                "\t\"nickName\":\"XXX\",\n" +
                "\t\"userName\":\"XXX\",\n" +
                "\t\"realName\":\"XXX\",\n" +
                "\t\"salary\":\"XXX\",\n" +
                "\t\"age\":\"XXX\"\n" +
                "}";


        initRSAKeyPair();

        String encode = encryptByRSAPublic(encodingString, publicKey);

        String decode = decryptByRSAPrivate(encode, privateKey);

        System.out.println("encode:" + encode);
        System.out.println("decode:" + decode);

        String aesEncode = AES(encodingString, "123456", Cipher.ENCRYPT_MODE);
        System.out.println("aesEncode:" + aesEncode);
        String aesDecode = AES(aesEncode, "123456", Cipher.DECRYPT_MODE);
        System.out.println("aesDecode:" + aesDecode);


    }

}
